package com.mf.utils;

import com.mf.entity.RecommendCode;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author: lmf
 * @Create: 2024/5/2 15:05
 * Description:
 */
public class CCICalculator {
    /**
     * 计算CCI（商品通道指数）
     *
     * @param recommendCodes 包含推荐代码的列表，每个推荐代码包含高、低、收盘价
     * @param period 计算CCI的周期数
     * @return 包含计算得到的CCI值的列表
     * @throws IllegalArgumentException 如果输入数据为空或不足以计算CCI
     */
    public static List<Double> calculateCCI(List<RecommendCode> recommendCodes, int period) {
        // 检查输入数据是否为空或不足以计算CCI
        if (recommendCodes == null || recommendCodes.size() < period) {
            return null;
//            throw new IllegalArgumentException("数据点不足以计算CCI。");
        }

        List<Double> cciValues = new ArrayList<>();
        double[] typicalPrices = new double[recommendCodes.size()]; // 存储典型价格的数组
        // TP MA
        double[] smaTP = new double[recommendCodes.size()]; // 存储典型价格移动平均值的数组
        double sumTP = 0; // 典型价格的累加器
        double sumSMA = 0; // 移动平均值的累加器


        // 计算每个推荐代码的典型价格
        for (int i = 0; i < recommendCodes.size(); i++) {
            RecommendCode rc = recommendCodes.get(i);
            typicalPrices[i] = (rc.getH() + rc.getL() + rc.getC()) / 3.0;
        }

        // 计算典型价格的初始简单移动平均值
        for (int i = 0; i < period; i++) {
            sumTP += typicalPrices[i];
        }
        // 设置第一个周期的 TP MA
        smaTP[period - 1] = sumTP / period;
        sumSMA = sumTP; // 初始化移动平均值的累加器

        // 计算后续周期的典型价格移动平均值
        for (int i = period; i < recommendCodes.size(); i++) {
            // 更新移动平均值累加器
            sumSMA += typicalPrices[i] - typicalPrices[i - period];
            // 计算新的移动平均值
            smaTP[i] = sumSMA / period;
        }

        // 计算平均绝对偏差
        double sumDeviation = 0;
        double meanDeviation;

        for (int i = period - 1; i < recommendCodes.size(); i++) {
            double deviation = typicalPrices[i] - smaTP[i];
            sumDeviation += Math.abs(deviation);
        }
        meanDeviation = sumDeviation / (recommendCodes.size() - period + 1);

        // 计算并存储CCI值
        for (int i = 0; i < recommendCodes.size(); i++) {
            if (i < period - 1) {
                cciValues.add(0.0); // 还未充分计算的周期添加为0
            } else {
                // 避免除以零的错误
                if (meanDeviation != 0) {
                    double cci = (typicalPrices[i] - smaTP[i]) / (0.015 * meanDeviation);
                    cciValues.add(cci);
                } else {
                    // 如果平均绝对偏差为零，添加特殊值表示无定义
                    cciValues.add(0.0); // 使用NaN表示无定义的值
                }
            }
        }

        return cciValues;
    }
}
